/*
 * Copyright © 2002 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * California 95054, U.S.A. All rights reserved.  Sun Microsystems, Inc. has
 * intellectual property rights relating to technology embodied in the product
 * that is described in this document. In particular, and without limitation,
 * these intellectual property rights may include one or more of the U.S.
 * patents listed at http://www.sun.com/patents and one or more additional
 * patents or pending patent applications in the U.S. and in other countries.
 * U.S. Government Rights - Commercial software. Government users are subject
 * to the Sun Microsystems, Inc. standard license agreement and applicable
 * provisions of the FAR and its supplements.  Use is subject to license terms.
 * Sun,  Sun Microsystems,  the Sun logo and  Java are trademarks or registered
 * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.  This
 * product is covered and controlled by U.S. Export Control laws and may be
 * subject to the export or import laws in other countries.  Nuclear, missile,
 * chemical biological weapons or nuclear maritime end uses or end users,
 * whether direct or indirect, are strictly prohibited.  Export or reexport
 * to countries subject to U.S. embargo or to entities identified on U.S.
 * export exclusion lists, including, but not limited to, the denied persons
 * and specially designated nationals lists is strictly prohibited.
 */

package org.javacc.parser;

import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * A class with static state that stores all option information.
 */
public class Options {

	/**
	 * Limit subclassing to derived classes.
	 */
	protected Options() {
	}

	/**
	 * A mapping of option names (Strings) to values (Integer, Boolean, String).
	 * This table is initialized by the main program. Its contents defines the
	 * set of legal options. Its initial values define the default option
	 * values, and the option types can be determined from these values too.
	 */
	protected static Map optionValues = null;

	/**
	 * Convenience method to retrieve integer options.
	 */
	protected static int intValue(final String option) {
		return ((Integer) optionValues.get(option)).intValue();
	}

	/**
	 * Convenience method to retrieve boolean options.
	 */
	protected static boolean booleanValue(final String option) {
		return ((Boolean) optionValues.get(option)).booleanValue();
	}

	/**
	 * Convenience method to retrieve string options.
	 */
	protected static String stringValue(final String option) {
		return (String) optionValues.get(option);
	}

	/**
	 * Keep track of what options were set as a command line argument. We use
	 * this to see if the options set from the command line and the ones set in
	 * the input files clash in any way.
	 */
	private static Set cmdLineSetting = null;

	/**
	 * Keep track of what options were set from the grammar file. We use this to
	 * see if the options set from the command line and the ones set in the
	 * input files clash in any way.
	 */
	private static Set inputFileSetting = null;

	/**
	 * Initialize for JavaCC
	 */
	public static void init() {
		optionValues = new HashMap();
		cmdLineSetting = new HashSet();
		inputFileSetting = new HashSet();

		optionValues.put("LOOKAHEAD", new Integer(1));
		optionValues.put("CHOICE_AMBIGUITY_CHECK", new Integer(2));
		optionValues.put("OTHER_AMBIGUITY_CHECK", new Integer(1));

		optionValues.put("STATIC", Boolean.TRUE);
		optionValues.put("DEBUG_PARSER", Boolean.FALSE);
		optionValues.put("DEBUG_LOOKAHEAD", Boolean.FALSE);
		optionValues.put("DEBUG_TOKEN_MANAGER", Boolean.FALSE);
		optionValues.put("ERROR_REPORTING", Boolean.TRUE);
		optionValues.put("JAVA_UNICODE_ESCAPE", Boolean.FALSE);
		optionValues.put("UNICODE_INPUT", Boolean.FALSE);
		optionValues.put("IGNORE_CASE", Boolean.FALSE);
		optionValues.put("USER_TOKEN_MANAGER", Boolean.FALSE);
		optionValues.put("USER_CHAR_STREAM", Boolean.FALSE);
		optionValues.put("BUILD_PARSER", Boolean.TRUE);
		optionValues.put("BUILD_TOKEN_MANAGER", Boolean.TRUE);
		optionValues.put("TOKEN_MANAGER_USES_PARSER", Boolean.FALSE);
		optionValues.put("SANITY_CHECK", Boolean.TRUE);
		optionValues.put("FORCE_LA_CHECK", Boolean.FALSE);
		optionValues.put("COMMON_TOKEN_ACTION", Boolean.FALSE);
		optionValues.put("CACHE_TOKENS", Boolean.FALSE);
		optionValues.put("KEEP_LINE_COLUMN", Boolean.TRUE);

		optionValues.put("OUTPUT_DIRECTORY", ".");
		optionValues.put("JDK_VERSION", "1.4");
	}

	/**
	 * Determine if a given command line argument might be an option flag.
	 * Command line options start with a dash&nbsp;(-).
	 * 
	 * @param opt
	 *            The command line argument to examine.
	 * @return True when the argument looks like an option flag.
	 */
	public static boolean isOption(final String opt) {
		return opt != null && opt.length() > 1 && opt.charAt(0) == '-';
	}

	public static void setInputFileOption(Object nameloc, Object valueloc,
			String name, int value) {
		String s = name.toUpperCase();
		if (!optionValues.containsKey(s)) {
			JavaCCErrors.warning(nameloc, "Bad option name \"" + name
					+ "\".  Option setting will be ignored.");
			return;
		}
		Object Val = optionValues.get(s);
		if (Val != null) {
			if (!(Val instanceof Integer) || value <= 0) {
				JavaCCErrors.warning(valueloc, "Bad option value \"" + value
						+ "\" for \"" + name
						+ "\".  Option setting will be ignored.");
				return;
			}
			if (inputFileSetting.contains(s)) {
				JavaCCErrors.warning(nameloc, "Duplicate option setting for \""
						+ name + "\" will be ignored.");
				return;
			}
			if (cmdLineSetting.contains(s)) {
				if (((Integer) Val).intValue() != value) {
					JavaCCErrors.warning(nameloc, "Command line setting of \""
							+ name + "\" modifies option value in file.");
				}
				return;
			}
		}

		optionValues.put(s, new Integer(value));
		inputFileSetting.add(s);
	}

	public static void setInputFileOption(Object nameloc, Object valueloc,
			String name, boolean value) {
		String s = name.toUpperCase();
		if (!optionValues.containsKey(s)) {
			JavaCCErrors.warning(nameloc, "Bad option name \"" + name
					+ "\".  Option setting will be ignored.");
			return;
		}
		Object Val = optionValues.get(s);
		if (Val != null) {
			if (!(Val instanceof Boolean)) {
				JavaCCErrors.warning(valueloc, "Bad option value \"" + value
						+ "\" for \"" + name
						+ "\".  Option setting will be ignored.");
				return;
			}
			if (inputFileSetting.contains(s)) {
				JavaCCErrors.warning(nameloc, "Duplicate option setting for \""
						+ name + "\" will be ignored.");
				return;
			}
			if (cmdLineSetting.contains(s)) {
				if (((Boolean) Val).booleanValue() != value) {
					JavaCCErrors.warning(nameloc, "Command line setting of \""
							+ name + "\" modifies option value in file.");
				}
				return;
			}
		}

		optionValues.put(s, (value ? Boolean.TRUE : Boolean.FALSE));
		inputFileSetting.add(s);
	}

	public static void setInputFileOption(Object nameloc, Object valueloc,
			String name, String value) {
		String s = name.toUpperCase();
		if (!optionValues.containsKey(s)) {
			JavaCCErrors.warning(nameloc, "Bad option name \"" + name
					+ "\".  Option setting will be ignored.");
			return;
		}
		Object Val = optionValues.get(s);
		if (Val != null) {
			if (!(Val instanceof String)) {
				JavaCCErrors.warning(valueloc, "Bad option value \"" + value
						+ "\" for \"" + name
						+ "\".  Option setting will be ignored.");
				return;
			}
			if (inputFileSetting.contains(s)) {
				JavaCCErrors.warning(nameloc, "Duplicate option setting for \""
						+ name + "\" will be ignored.");
				return;
			}
			if (cmdLineSetting.contains(s)) {
				if (!Val.equals(value)) {
					JavaCCErrors.warning(nameloc, "Command line setting of \""
							+ name + "\" modifies option value in file.");
				}
				return;
			}
		}

		optionValues.put(s, value);
		inputFileSetting.add(s);
	}

	/**
	 * 
	 * @param arg
	 */
	public static void setCmdLineOption(String arg) {
		String s = arg.toUpperCase();
		int index = 0;
		String name;
		Object Val;
		while (index < s.length() && s.charAt(index) != '='
				&& s.charAt(index) != ':') {
			index++;
		}
		if (index < 2 || index >= s.length() - 1) {
			if (index == s.length()) {
				if (s.length() > 3 && s.charAt(1) == 'N' && s.charAt(2) == 'O') {
					name = s.substring(3);
					Val = Boolean.FALSE;
				} else {
					name = s.substring(1);
					Val = Boolean.TRUE;
				}
			} else {
				System.out.println("Warning: Bad option \"" + arg
						+ "\" will be ignored.");
				return;
			}
		} else {
			if (s.substring(index + 1).equals("TRUE")) {
				Val = Boolean.TRUE;
			} else if (s.substring(index + 1).equals("FALSE")) {
				Val = Boolean.FALSE;
			} else {
				try {
					int i = Integer.parseInt(s.substring(index + 1));
					if (i <= 0) {
						System.out.println("Warning: Bad option value in \""
								+ arg + "\" will be ignored.");
						return;
					}
					Val = new Integer(i);
				} catch (NumberFormatException e) {
					Val = arg.substring(index + 1);
					if (arg.length() > index + 2) {
						// i.e., there is space for two '"'s in value
						if (arg.charAt(index + 1) == '"'
								&& arg.charAt(arg.length() - 1) == '"') {
							// remove the two '"'s.
							Val = arg.substring(index + 2, arg.length() - 1);
						}
					}
				}
			}
			name = s.substring(1, index);
		}
		if (!optionValues.containsKey(name)) {
			System.out.println("Warning: Bad option \"" + arg
					+ "\" will be ignored.");
			return;
		}
		Object valOrig = optionValues.get(name);
		if (Val.getClass() != valOrig.getClass()) {
			System.out.println("Warning: Bad option value in \"" + arg
					+ "\" will be ignored.");
			return;
		}
		if (cmdLineSetting.contains(name)) {
			System.out.println("Warning: Duplicate option setting \"" + arg
					+ "\" will be ignored.");
			return;
		}

		optionValues.put(name, Val);
		cmdLineSetting.add(name);
	}

	public static void normalize() {
		if (getDebugLookahead() && !getDebugParser()) {
			if (cmdLineSetting.contains("DEBUG_PARSER")
					|| inputFileSetting.contains("DEBUG_PARSER")) {
				JavaCCErrors
						.warning("True setting of option DEBUG_LOOKAHEAD overrides false setting of option DEBUG_PARSER.");
			}
			optionValues.put("DEBUG_PARSER", Boolean.TRUE);
		}
	}

	/**
	 * Find the lookahead setting.
	 * 
	 * @return The requested lookahead value.
	 */
	public static int getLookahead() {
		return intValue("LOOKAHEAD");
	}

	/**
	 * Find the choice ambiguity check value.
	 * 
	 * @return The requested choice ambiguity check value.
	 */
	public static int getChoiceAmbiguityCheck() {
		return intValue("CHOICE_AMBIGUITY_CHECK");
	}

	/**
	 * Find the other ambiguity check value.
	 * 
	 * @return The requested other ambiguity check value.
	 */
	public static int getOtherAmbiguityCheck() {
		return intValue("OTHER_AMBIGUITY_CHECK");
	}

	/**
	 * Find the static value.
	 * 
	 * @return The requested static value.
	 */
	public static boolean getStatic() {
		return booleanValue("STATIC");
	}

	/**
	 * Find the debug parser value.
	 * 
	 * @return The requested debug parser value.
	 */
	public static boolean getDebugParser() {
		return booleanValue("DEBUG_PARSER");
	}

	/**
	 * Find the debug lookahead value.
	 * 
	 * @return The requested debug lookahead value.
	 */
	public static boolean getDebugLookahead() {
		return booleanValue("DEBUG_LOOKAHEAD");
	}

	/**
	 * Find the debug tokenmanager value.
	 * 
	 * @return The requested debug tokenmanager value.
	 */
	public static boolean getDebugTokenManager() {
		return booleanValue("DEBUG_TOKEN_MANAGER");
	}

	/**
	 * Find the error reporting value.
	 * 
	 * @return The requested error reporting value.
	 */
	public static boolean getErrorReporting() {
		return booleanValue("ERROR_REPORTING");
	}

	/**
	 * Find the Java unicode escape value.
	 * 
	 * @return The requested Java unicode escape value.
	 */
	public static boolean getJavaUnicodeEscape() {
		return booleanValue("JAVA_UNICODE_ESCAPE");
	}

	/**
	 * Find the unicode input value.
	 * 
	 * @return The requested unicode input value.
	 */
	public static boolean getUnicodeInput() {
		return booleanValue("UNICODE_INPUT");
	}

	/**
	 * Find the ignore case value.
	 * 
	 * @return The requested ignore case value.
	 */
	public static boolean getIgnoreCase() {
		return booleanValue("IGNORE_CASE");
	}

	/**
	 * Find the user tokenmanager value.
	 * 
	 * @return The requested user tokenmanager value.
	 */
	public static boolean getUserTokenManager() {
		return booleanValue("USER_TOKEN_MANAGER");
	}

	/**
	 * Find the user charstream value.
	 * 
	 * @return The requested user charstream value.
	 */
	public static boolean getUserCharStream() {
		return booleanValue("USER_CHAR_STREAM");
	}

	/**
	 * Find the build parser value.
	 * 
	 * @return The requested build parser value.
	 */
	public static boolean getBuildParser() {
		return booleanValue("BUILD_PARSER");
	}

	/**
	 * Find the build token manager value.
	 * 
	 * @return The requested build token manager value.
	 */
	public static boolean getBuildTokenManager() {
		return booleanValue("BUILD_TOKEN_MANAGER");
	}

	/**
	 * Find the token manager uses parser value.
	 * 
	 * @return The requested token manager uses parser value;
	 */
	public static boolean getTokenManagerUsesParser() {
		return booleanValue("TOKEN_MANAGER_USES_PARSER");
	}

	/**
	 * Find the sanity check value.
	 * 
	 * @return The requested sanity check value.
	 */
	public static boolean getSanityCheck() {
		return booleanValue("SANITY_CHECK");
	}

	/**
	 * Find the force lookahead check value.
	 * 
	 * @return The requested force lookahead value.
	 */
	public static boolean getForceLaCheck() {
		return booleanValue("FORCE_LA_CHECK");
	}

	/**
	 * Find the common token action value.
	 * 
	 * @return The requested common token action value.
	 */

	public static boolean getCommonTokenAction() {
		return booleanValue("COMMON_TOKEN_ACTION");
	}

	/**
	 * Find the cache tokens value.
	 * 
	 * @return The requested cache tokens value.
	 */
	public static boolean getCacheTokens() {
		return booleanValue("CACHE_TOKENS");
	}

	/**
	 * Find the keep line column value.
	 * 
	 * @return The requested keep line column value.
	 */
	public static boolean getKeepLineColumn() {
		return booleanValue("KEEP_LINE_COLUMN");
	}

	/**
	 * Find the JDK version
	 * 
	 * @return The requested jdk version.
	 */
	public static String getJdkVersion() {
		return stringValue("JDK_VERSION");
	}

	/**
	 * Find the output directory.
	 * 
	 * @return The requested output directory.
	 */
	public static File getOutputDirectory() {
		return new File(stringValue("OUTPUT_DIRECTORY"));
	}
}
